﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Experimentor
{
    public abstract class UserAction : SeqPartWorkerListener
    {
        protected string _name;
        protected string _description;
        protected List<string> _savesurfixes;
        protected SeqPartWorker _parentWorker;
        protected DatasaveObject _datasaveObject;
        protected Control _slidecontrol;
        protected string _savetable;
        protected string _savecolumn;
        
        public string Name
        {
            get { return this._name; }
            private set { this._name = value; }
        }

        public string Description
        {
            get { return this._description; }
            private set { this._description = value; }
        }

        public List<string> Savesurfixes
        {
            get { return this._savesurfixes; }
            private set { }
        }

        public abstract Control getControl(SeqPartWorker worker, Control slidecontrol, DatasaveObject saveobject, string savetable, string savecolumn);

        public void disableParent()
        {
            this._parentWorker = null;
        }

        public Point GetPositionInForm(Control ctrl)
        {
            Point p = ctrl.Location;
            Control parent = ctrl.Parent;
            while (!(parent is Form))
            {
                p.Offset(parent.Location.X, parent.Location.Y);
                parent = parent.Parent;
            }
            return p;
        }
    }

    public class UserAction_None : UserAction
    {
        public UserAction_None()
        {
            this._name = "NONE";
            this._description = "No user action";
            this._savesurfixes = new List<string>();
        }

        public override Control getControl(SeqPartWorker worker, Control slidecontrol, DatasaveObject saveobject, string savetable, string savecolumn)
        {
            return null;
        }
    }

    public class UserAction_ClickNext : UserAction
    {
        public UserAction_ClickNext()
        {
            this._name = "Click Next";
            this._description = "Click the next button";
            this._savesurfixes = new List<string>();
        }

        public override Control getControl(SeqPartWorker worker, Control slidecontrol, DatasaveObject saveobject, string savetable, string savecolumn)
        {
            this._parentWorker = worker;
            this._parentWorker.registerListener(this);
            Button button = new Button();
            button.BackColor = SystemColors.Control;
            button.Text = "Weiter";
            button.Click += delegate(object sender, EventArgs e)
            {
                if (this._parentWorker != null) this._parentWorker.mre.Set();
            };
            return button;
        }
    }

    public class UserAction_ClickPicture : UserAction
    {
        public UserAction_ClickPicture()
        {
            this._name = "Click Picture";
            this._description = "Click on a picture to choose";
            this._savesurfixes = new List<string>() { "_click" };
        }

        public override Control getControl(SeqPartWorker worker, Control slidecontrol, DatasaveObject saveobject, string savetable, string savecolumn)
        {
            this._parentWorker = worker;
            this._parentWorker.registerListener(this);
            this._datasaveObject = saveobject;
            this._savetable = savetable;
            this._savecolumn = savecolumn;
            slidecontrol.Click += delegate(object sender, EventArgs e)
            {
                if (this._datasaveObject!=null) this._datasaveObject.addSaveObject(this._savetable, this._savecolumn + "_click", 1);
                if (this._parentWorker != null) this._parentWorker.mre.Set();
            };
            if (this._datasaveObject != null) this._datasaveObject.addSaveObject(this._savetable, this._savecolumn + "_click", 0);
            return null;
        }
    }

    public class UserAction_ClickLeftRight : UserAction, IMessageFilter
    {
        private bool _isLeft = true;
        
        public UserAction_ClickLeftRight()
        {
            this._name = "Click Left or Right";
            this._description = "Click left or right mousebutton (anywhere in Window)";
            this._savesurfixes = new List<string>() { "_click" };
        }

        public override Control getControl(SeqPartWorker worker, Control slidecontrol, DatasaveObject saveobject, string savetable, string savecolumn)
        {
            this._parentWorker = worker;
            this._parentWorker.registerListener(this);
            this._datasaveObject = saveobject;
            this._savetable = savetable;
            this._savecolumn = savecolumn;
            this._slidecontrol = slidecontrol;
            //das ist wirklich gebastelt, aber anhand der Position der slidecontrol wird entschieden, ob es "links" oder "rechts" ist
            Point locationOnForm = this.GetPositionInForm(this._slidecontrol);
            if (locationOnForm.X >= this._slidecontrol.FindForm().Width / 2) this._isLeft = false;
            Application.AddMessageFilter(this);
            if (this._datasaveObject != null) this._datasaveObject.addSaveObject(this._savetable, this._savecolumn + "_click", -1);
            return null;
        }

        public bool PreFilterMessage (ref Message message)
        {
            if (this._slidecontrol.IsDisposed || this._slidecontrol.Parent==null)
            {
                Application.RemoveMessageFilter(this);
                return false;
            }
            if (message.Msg == 0x0202 || message.Msg == 0x0205)
            {
                if (this._datasaveObject != null)
                {
                    int correct = 0;
                    if ((this._isLeft && message.Msg == 0x0202) || (!this._isLeft && message.Msg == 0x0205)) correct = 1;
                    this._datasaveObject.addSaveObject(this._savetable, this._savecolumn + "_click", correct);
                }
                if (this._parentWorker != null) this._parentWorker.mre.Set();
                return true;
            }
            return false;
        }
    }
    
    public class UserAction_DrawMatches : UserAction
    {
        private Bitmap _curDrawing;
        private Bitmap _tmpBmp;
        private Point _lastMouseDown;
        private bool _mouseIsDown = false;
        
        public UserAction_DrawMatches()
        {
            this._name = "Draw Matches";
            this._description = "Draw matches for solution";
            this._savesurfixes = new List<string>() { "_file", "_sec" };
        }

        public override Control getControl(SeqPartWorker worker, Control slidecontrol, DatasaveObject saveobject, string savetable, string savecolumn)
        {
            //prepare the threads
            this._parentWorker = worker;
            this._parentWorker.registerListener(this);
            this._datasaveObject = saveobject;
            this._savetable = savetable;
            this._savecolumn = savecolumn;
            Stopwatch watch = new Stopwatch();
            //prepare the control
            Panel panel_main = new Panel();
            panel_main.Size = new Size(slidecontrol.Width < 1000 ? slidecontrol.Width : 1000, slidecontrol.Height);
            Label label_drawhere = new Label();
            label_drawhere.AutoSize = true;
            label_drawhere.Text = "Bitte zeichnen Sie im umrandeten Feld.\nZum Zeichnen drücken Sie mit der linken Maustaste im Feld, halten Sie die Maustaste gedrückt und ziehen Sie zu dem Punkt im Feld, wohin eine Linie gehen soll.\nSie können alle bisherigen Linien löschen, indem Sie auf 'Löschen' klicken.\nWenn Sie fertig sind, klicken Sie bitte auf 'Weiter'.";
            label_drawhere.Location = new Point(0, 0);
            panel_main.Controls.Add(label_drawhere);
            this._curDrawing = new Bitmap(slidecontrol.Width < 1000 ? slidecontrol.Width-100 : 900, slidecontrol.Height - 120);
            this._tmpBmp = new Bitmap(slidecontrol.Width < 1000 ? slidecontrol.Width - 100 : 900, slidecontrol.Height - 120);
            DoubleBufferedPanel panel_draw = new DoubleBufferedPanel();
            panel_draw.Size = new Size(slidecontrol.Width < 1000 ? slidecontrol.Width - 100 : 900, slidecontrol.Height - 120);
            panel_draw.BackColor = Color.White;
            panel_draw.BorderStyle = BorderStyle.FixedSingle;
            panel_draw.MouseDown += delegate(object sender, MouseEventArgs e)
            {
                this._lastMouseDown = e.Location;
                this._mouseIsDown = true;
            };
            panel_draw.MouseMove += delegate(object sender, MouseEventArgs e)
            {
                if (e.Button==MouseButtons.Left)
                {
                    this._tmpBmp = this._curDrawing.Clone() as Bitmap;
                    using (Graphics g = Graphics.FromImage(this._tmpBmp))
                    {
                        g.DrawLine(new Pen(Color.Black, (float)1.0), this._lastMouseDown, e.Location);
                    }
                    panel_draw.Refresh();
                }
            };
            panel_draw.MouseUp += delegate(object sender, MouseEventArgs e)
            {
                using (Graphics g = Graphics.FromImage(this._curDrawing))
                {
                    g.DrawLine(new Pen(Color.Black, (float)3.0), this._lastMouseDown, e.Location);
                }
                this._mouseIsDown = false;
                panel_draw.Invalidate();
            };
            panel_draw.Paint += delegate(object sender, PaintEventArgs e)
            {
                if (this._mouseIsDown) e.Graphics.DrawImage(this._tmpBmp, Point.Empty);
                else e.Graphics.DrawImage(this._curDrawing, Point.Empty);
            };
            panel_draw.Location = new Point(0, 70);
            panel_main.Controls.Add(panel_draw);
            Button button_clear = new Button();
            button_clear.BackColor = SystemColors.Control;
            button_clear.Text = "Löschen";
            button_clear.Click += delegate(object sender, EventArgs e)
            {
                this._curDrawing = new Bitmap(slidecontrol.Width < 1000 ? slidecontrol.Width - 100 : 900, slidecontrol.Height - 120);
                panel_draw.Invalidate();
            };
            button_clear.Width = 80;
            button_clear.Location = new Point(slidecontrol.Width < 1000 ? slidecontrol.Width-80 : 920, 70);
            panel_main.Controls.Add(button_clear);
            Button button_finished = new Button();
            button_finished.BackColor = SystemColors.Control;
            button_finished.Text = "Weiter";
            button_finished.Click += delegate(object sender, EventArgs e)
            {
                DialogResult dr = MessageBox.Show("Sind Sie mit dem Zeichnen fertig?\n\n(Wenn Sie 'Ja' klicken, wird das Bild so gespeichert.)", "Bitte bestätigen ...", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (dr == DialogResult.Yes)
                {
                    if (this._datasaveObject != null)
                    {
                        string bmpfilename = this._datasaveObject.Directory + Path.DirectorySeparatorChar + this._savecolumn + "_" + this._datasaveObject.Session + ".bmp";
                        if (!this._datasaveObject.Preview) this._curDrawing.Save(bmpfilename);
                        this._datasaveObject.addSaveObject(this._savetable, this._savecolumn + "_file", new Uri(bmpfilename));
                        this._datasaveObject.addSaveObject(this._savetable, this._savecolumn + "_sec", watch.ElapsedMilliseconds / 1000);
                    }
                    if (this._parentWorker!=null) this._parentWorker.mre.Set();
                }
            };
            button_finished.Width = 80;
            button_finished.Location = new Point(slidecontrol.Width < 1000 ? slidecontrol.Width - 80 : 920, slidecontrol.Height - 50 - button_finished.Height);
            panel_main.Controls.Add(button_finished);
            //start the stopwatch (assumes that the control is immediately drawn)
            watch.Start();
            //return the control
            return panel_main;
        }

        class DoubleBufferedPanel : Panel
        {
            public DoubleBufferedPanel() : base()
            {
                this.DoubleBuffered = true;
            }
        }

    }

}
